/*
 * Albareth - an action roleplaying game.
 * Copyright (C) 2008 Jens Anuth
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 *   02110-1301, USA.
 * 
 * $Id: MapGenerator.java 37 2008-05-06 19:30:07Z jens464 $
*/
package albareth.generator;

import java.util.Iterator;
import java.util.List;

import albareth.core.GameMap;
import albareth.core.LevelSpawnConfiguration;
import albareth.core.NoLocationAvailableException;
import albareth.core.entity.Ground;
import albareth.core.entity.Item;
import albareth.core.entity.Mobile;
import albareth.util.Location;
import frosch.util.Log;
import frosch.util.Rectangle;

public abstract class MapGenerator
{
    public static final Ground DEFAULT_FLOOR = Ground.FLOOR_LIGHT1;
    public static final Ground DEFAULT_WALL = Ground.WALL1;
    
    protected GameMap map = null;
    protected Location entranceLocation = null;
    protected Location exitLocation = null;
    protected LevelSpawnConfiguration spawnConfig = null;

    public GameMap generate(int width, int height,
            LevelSpawnConfiguration config)
    {
        map = new GameMap(width, height);
        spawnConfig = config;
        try {
            generateMap(width, height);
        } catch (MapGenerationFailureException e) {
            Log.warn("Map generation failed: " + e);
            Log.info("Trying to reroll level.");
            return generate(width, height, config);
        }
        
        if (getEntranceLocation() == null || getExitLocation() == null)
        {
            throw new RuntimeException(
                    "MapGenerator implementation must " +
                    "provide both entranceLocation and exitLocation!"
            );
        }
        map.setEntranceLocation(getEntranceLocation());
        map.setExitLocation(getExitLocation());
        
        return map;
    }

    protected abstract void generateMap(int width, int height) throws
            MapGenerationFailureException;
    
    protected Location getEntranceLocation()
    {
        return entranceLocation;
    }
    
    protected Location getExitLocation()
    {
        return exitLocation;
    }
    
    
    /**
     * A rectangular room in a dungeon.
     */
    class Room extends Rectangle
    {
        GameMap map;
        
        Room(int x, int y, int w, int h, GameMap map)
        {
            super(x, y, w, h);
            this.map = map;
        }
        
        Location getRandomLocation() throws NoLocationAvailableException
        {
            return map.getRandomLocation(this);
        }
        
        void drawSelf()
        {
            MapGeneratorTools.fillMapRect(map, x, y, w, h, DEFAULT_FLOOR);
        }
        
        public void fillWithMonsterSpawn()
        {
            //fill with monsters:
            List mobs = spawnConfig.getRoomMobileSpawn(getArea());
            for (Iterator it = mobs.iterator(); it.hasNext(); )
            {
                try {
                    Mobile mob = (Mobile)it.next();
                    Location loc = getRandomLocation();
                    mob.moveTo(loc);
                    
                } catch (NoLocationAvailableException e) {
                    Log.warn("Could not place monster(s): " + e);
                    break;
                }
            }
        }
        
        public void fillWithItemSpawn()
        {
            List items = spawnConfig.getRoomItemSpawn(getArea());
            for (Iterator it = items.iterator(); it.hasNext(); )
            {
                try {
                    Item item = (Item)it.next();
                    Location loc = getRandomLocation();
                    item.moveTo(loc);
                    
                } catch (NoLocationAvailableException e) {
                    Log.warn("Could not place item(s): " + e);
                    break;
                }
            }
        }

        /**
         * Removes all mobiles from this room.
         */
        public void removeAllMobiles()
        {
            for (int i = getX(); i <= getRightX(); i++)
            {
                for (int j = getY(); j <= getBottomY(); j++)
                {
                    map.removeMobile(i, j);
                }
            }
        }

    }
}
